package mdc

import (
	"fmt"
	"go.uber.org/zap"
	"strconv"
	"sync"
	"wzgames/internal/constant"
	"wzgames/internal/model/dwp"
	"wzgames/internal/model/login"
	"wzgames/internal/model/user"
	"wzgames/internal/services/games"
	"wzgames/pkg/actor/actor"
	"wzgames/pkg/actor/actorservice"
	"wzgames/pkg/logger"
	"wzgames/pkg/server/ws"
	"wzgames/pkg/utils/dateutil"
)

// 玩家 Actor
// 玩家在一个游戏运行节点上一个Actor可以管理多个游戏的登录

type Player struct {
	pid      *actor.PID
	showName string
	showId   string
	agt      *HallAgent      // 所属Agent
	uCache   *user.UserCache // 缓存的用户信息

	mu         sync.RWMutex
	gameTables map[int64]*games.GameTable // 进入的游戏实例

	activeTime int64
}

func NewPlayer(u *user.UserCache, pt *HallAgent) *Player {
	p := &Player{
		uCache:     u,
		agt:        pt,
		showId:     fmt.Sprintf("%v_%s", u.Uid, pt.AgtKey()),
		showName:   fmt.Sprintf("%s_%s", pt.AgtKey(), u.Uname),
		gameTables: make(map[int64]*games.GameTable),
		activeTime: dateutil.UnixMilli(),
	}

	name := fmt.Sprintf("p-%s-%s", strconv.FormatInt(u.AgtId, 10), strconv.FormatUint(u.Uid, 10))
	props := actor.PropsFromProducer(func() actor.Actor { return p })
	pid, err := actorservice.ActorSystem().Root.SpawnNamed(props, name)
	if err != nil {
		logger.Error("[MDC] spawn player actor error", zap.Int64("AgtId", u.AgtId), zap.Uint64("PlayerID", u.Uid), zap.Error(err))
		p = nil
		return nil
	}
	p.pid = pid

	return p
}

// ID 玩家在G端的用户ID
func (p *Player) ID() uint64 {
	return p.uCache.Uid
}

func (p *Player) Pid() *actor.PID {
	return p.pid
}

func (p *Player) AgtID() int64 {
	return p.uCache.AgtId
}

func (p *Player) Name() string {
	return p.uCache.Uname
}

// AUid 玩家在B端的用户ID
func (p *Player) AUid() string {
	return p.uCache.AgtUid
}

func (p *Player) UInfo() *user.UserCache {
	return p.uCache
}

func (p *Player) ShowID() string {
	return p.showId
}

func (p *Player) ShowName() string {
	return p.showName
}

func (p *Player) UHead() string {
	return p.uCache.Uhead
}

func (p *Player) AgtKey() string {
	return p.agt.AgtKey()
}

func (p *Player) Receive(ctx actor.Context) {
	switch m := ctx.Message().(type) {
	case *games.GameTable: // 登录游戏
		p.loginGame(m)
	case *ws.MsgEvent: // 处理登录后的客户端消息
		p.handleExControl(m)
	case *games.BetDelay: // 新轮盘的延迟结算
		p.handleBetDelay(m)
	case constant.ActiveCheck: // 判断玩家是否处于活跃状态
		p.ActiveCheck()
	case constant.PingPong:
		p.Active()
	case constant.WSClosed: // 玩家断开某一游戏
		p.handleWsClose(m)
	case *login.LoginKickCheck: // kick clean
		p.loginKickCheck(m)
	case *dwp.BalanceChange: // 转账后尽可能的通知客户端余额变化
		p.checkBalanceChange(m.CurrId)
	case *games.AutoCashOutCheckPoint: // 小飞机自动CashOut结算处理
		p.handleAutoCashOut(m)
	}
}

func (p *Player) loginGame(newTB *games.GameTable) {
	defer logger.Recover()

	p.mu.RLock()
	tb, ok := p.gameTables[newTB.Inst.GameInstId()]
	p.mu.RUnlock()
	if ok { // 旧的游戏登录清除
		tb.Clean()
	}

	// 创建新的游戏状态
	p.mu.Lock()
	newTB.Player = p
	p.gameTables[newTB.Inst.GameInstId()] = newTB
	p.mu.Unlock()
	// 登录游戏实例
	newTB.Inst.Login(newTB)
	newTB.SetLogin(true)
	p.Active()
}

func (p *Player) handleWsClose(data constant.WSClosed) {
	defer logger.Recover()
	p.mu.Lock()
	defer p.mu.Unlock()
	if data.Handle > 0 {
		tb, ok := p.gameTables[data.Handle]
		if ok && tb.Login() { // 断开游戏连接后清除状态
			tb.SetLogin(false)
			tb.Inst.Logout(tb)
		}
	}
}

func (p *Player) handleExControl(data *ws.MsgEvent) {
	defer logger.Recover()
	// 特殊处理 WebSocket连接后把登录的游戏实例ID缓存到WebSocket Session
	instId := data.Session().LinkID()
	p.mu.RLock()
	tb, ok := p.gameTables[instId]
	p.mu.RUnlock()
	if !ok || tb.SessID() != data.Session().ID() || !tb.Login() {
		return
	}
	// 处理来自客户端的消息
	tb.Inst.DoMessage(tb, data.Message())
	p.Active()
}

// handleBetDelay 新轮盘延时结算
func (p *Player) handleBetDelay(data *games.BetDelay) {
	defer logger.Recover()
	if data.Tb == nil || data.Tb.Inst == nil { //  || !data.Tb.Login()
		return
	}
	data.Tb.Inst.DoMessage(data.Tb, data)
	p.Active()
}

// handleAutoCashOut 小飞机Auto CashOut 结算
func (p *Player) handleAutoCashOut(data *games.AutoCashOutCheckPoint) {
	defer logger.Recover()

	if data.Tb == nil || data.Tb.Inst == nil {
		return
	}

	data.Tb.Inst.DoMessage(data.Tb, data)
}

func (p *Player) loginKickCheck(req *login.LoginKickCheck) {
	defer logger.Recover()

	p.mu.RLock()
	tb, ok := p.gameTables[req.InstId]
	p.mu.RUnlock()
	if ok {
		tb.Clean()
	}
}

// 通知余额变化
func (p *Player) checkBalanceChange(currId int32) {
	defer logger.Recover()

	p.mu.RLock()
	defer p.mu.RUnlock()
	for _, tb := range p.gameTables {
		if tb.Login() && tb.Seat.CurrId == currId {
			tb.LoadAndSendBalance()
		}
	}
}

func (p *Player) Active() {
	p.activeTime = dateutil.UnixSecs()
}

// ActiveCheck 活跃检查，超过半小时未活跃，登出该Actor下的所有游戏实例，并清除状态
func (p *Player) ActiveCheck() {
	if p.activeTime+dateutil.HalfHour < dateutil.UnixSecs() {
		p.stop()
	}
}

func (p *Player) stop() {
	p.mu.Lock()
	defer p.mu.Unlock()
	// 登出游戏实例
	for _, tb := range p.gameTables {
		tb.SetLogin(false)
		tb.Inst.Logout(tb)
		tb.Clean()
	}
	// 从Agent管理中移除该玩家
	p.agt.RemovePlayer(p.ID())

	actorservice.Stop(p.pid)
}
